Introduction
RNA-seq was run on 36 ovarian cancer cell lines, each in
singlicate.
All 36 cell lines have 72h cisplatin IC50s determined by Kristin
Adams and Kendra Wendt in the Lang Lab.
Add in links to Lab notebooks for IC50, RNAseq sample prep
DESeq2
To determine genes differentially expressed between cisplatin
sensitive and resistant cell lines, we used the median cisplatin IC50 of
all 36 cell lines as a cut-point, and excluded cell lines within +/- one
standard deviation of the median. These were defined in the metadata
table.
Load count matrix
countmatrix <- as.matrix(read.delim("../star_salmon/salmon.merged.gene_counts.tsv", sep="\t", row.names="gene_id"))
countmatrix <- countmatrix[,-1]
countmatrix2 <- matrix(as.numeric(countmatrix), ncol = ncol(countmatrix), dimnames = list(rownames(countmatrix), colnames(countmatrix)))
countmatrix2 <- round(countmatrix2)
countmatrix2 <- countmatrix2[,rownames(metadata)]
head(countmatrix2)
X16 X29 X14 X22 X21 X25 X20 X19 X28 X23 X27 X18 X31 X30 X17 X15 X2 X32 X13 X6 X11 X36
A1BG 28 21 52 45 48 39 8 66 20 36 0 52 2 4 27 54 0 16 37 26 14 35
A1BG-AS1 62 54 108 80 88 66 38 67 54 67 0 36 15 6 107 82 0 34 80 109 50 67
A1CF 1 2 0 0 0 2 0 0 0 0 0 0 0 0 0 0 7003 1 0 1 0 1
A2M 11 26 1161 0 10 8791 1 1 16 0 0 0 7 23 11639 0 8292 1 11 0 0 5
A2M-AS1 10 0 15 11 99 3 1 23 8 14 34 10 52 8 4 1 2 10 5 10 5 0
A2ML1 0 74 7 47 2 23 2 0 0 6 0 0 0 9 20 0 0 23 2 0 41 10
X10 X5 X8 X9 X24 X33 X34 X35 X26 X1 X12 X4 X3 X7
A1BG 14 30 2 32 4 1 9 14 4 137 30 58 0 0
A1BG-AS1 13 28 18 64 20 9 27 29 1 88 65 107 0 0
A1CF 0 0 0 0 0 0 0 0 0 0 0 0 0 0
A2M 0 38 0 0 0 113 3 89 0 0 11343 0 3 0
A2M-AS1 0 4 4 19 21 26 78 94 2 5 15 2 0 0
A2ML1 0 0 6 0 0 21 4 7 1 0 0 0 0 0
Create DESeqDataSet object dds
dds <- DESeqDataSetFromMatrix(countData = countmatrix2, colData = metadata, design = ~ Subtype + PlatinumSensitivity)
converting counts to integer mode
Warning: some variables in design formula are characters, converting to factors
Pre-filtering
This step removes genes with low expression to increase multiple
comparison power.
keep <- rowSums(counts(dds)) >= 500
dds <- dds[keep,]
nrow(dds)
[1] 15763
Run DESeq2
dds <- DESeq(dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
58 rows did not converge in beta, labelled in mcols(object)$betaConv. Use larger maxit argument with nbinomWaldTest
Print DESeq results
res <- results(dds, contrast=c("PlatinumSensitivity", "resistant", "sensitive"))
res
log2 fold change (MLE): PlatinumSensitivity resistant vs sensitive
Wald test p-value: PlatinumSensitivity resistant vs sensitive
DataFrame with 15763 rows and 6 columns
baseMean log2FoldChange lfcSE stat pvalue padj
<numeric> <numeric> <numeric> <numeric> <numeric> <numeric>
A1BG 25.5204 -0.219516891 0.771050 -0.284698733 0.7758750 0.961014
A1BG-AS1 46.1152 0.000262359 0.722143 0.000363305 0.9997101 0.999864
A1CF 206.3747 0.097577283 1.898634 0.051393412 0.9590120 0.995023
A2M 1083.7271 -1.036788003 1.548419 -0.669578303 NA NA
A2M-AS1 16.4917 1.644444544 0.798528 2.059345594 0.0394611 0.483442
... ... ... ... ... ... ...
ZYG11A 420.874 0.0349471 0.579837 0.0602706 0.951940 0.994151
ZYG11B 2150.578 -0.0101554 0.248877 -0.0408048 0.967452 0.995362
ZYX 4329.321 -0.4054512 0.302526 -1.3402200 0.180174 0.711910
ZZEF1 1675.205 0.1541036 0.221902 0.6944659 0.487390 0.883409
ZZZ3 3280.554 0.2194200 0.210781 1.0409865 0.297882 0.803258
write.csv(as.data.frame(res), file = "DESeq.csv")
Filter DESeq2 results for significant genes
Filter res for padj < 0.05 and |log2FC| >= 1.2
res.filtered <- as.data.frame(res) %>%
filter(padj<0.05)%>%
filter(log2FoldChange >= 1.2 | log2FoldChange <= -1.2)
res.filtered
Data QC
PCA plot
pcaData <- plotPCA(vsd, intgroup=c("Subtype", "PlatinumSensitivity","CellLine"), returnData=TRUE)
percentVar <- round(100 * attr(pcaData, "percentVar"))
myColors <- c("#76AB7E", "#63E678", "#1D32FB", "#7B87FD", "#01BD1F", "#E8A426", "#0B7C1D", "#BB19E7")
names(myColors) <- levels(pcaData$Subtype)
colScale <- scale_colour_manual(name = "Subtype",values = myColors)
pca <- ggplot(pcaData, aes(PC1, PC2, color=Subtype, shape=PlatinumSensitivity, label=CellLine)) +
geom_point(size=3) +
geom_text(hjust=0, vjust=0) +
xlab(paste0("PC1: ",percentVar[1],"% variance")) +
ylab(paste0("PC2: ",percentVar[2],"% variance")) +
coord_fixed() +
theme_classic() +
colScale
pca
ggsave("pca.pdf", pca)
Saving 7.29 x 4.51 in image

PCA plot groups samples roughly by subtype
Sample-wise correlation
vsd.mod <- vsd.df
colnames(vsd.mod) <- metadata$CellLine[match(colnames(vsd.mod), metadata$files)]
vsd.mod$gene <- row.names(vsd.mod)
corr <- vsd.mod %>%
select(-gene) %>%
cor(method = "spearman")
df.mod <- df
row.names(df.mod) <- metadata$CellLine[match(row.names(df.mod), metadata$files)]
pheatmap(corr, annotation_col=df.mod)

Barnes RF approach?
Contacted PI 10/27/2022 because script is not on github. No response
as of 12/16/2022.
Plot heatmaps to check sample to sample variability
Plotting the top 100 most highly expressed genes:
select <- order(rowMeans(counts(dds,normalized=TRUE)),
decreasing=TRUE)[1:100]
df <- as.data.frame(colData(dds)[,c("Subtype", "PlatinumSensitivity")])
pheatmap(assay(vsd)[select,], cluster_rows=FALSE, show_rownames=FALSE,
labels_col=colData(dds)[,c("CellLine")],annotation_col=df)

Dendrogram based on gene expression
Pull genes contributing to principal components 1 & 2
TPM <- as.matrix(read.delim("../star_salmon/salmon.merged.gene_tpm.tsv", sep="\t", row.names="gene_id"))
TPM <- TPM[,-1]
TPM <- matrix(as.numeric(TPM), ncol = ncol(TPM), dimnames = list(rownames(TPM), colnames(TPM)))
TPM <- round(TPM)
TPM <- TPM[,rownames(metadata)]
TPM2 <- TPM
colnames(TPM2) <- metadata$CellLine[match(colnames(TPM2), metadata$files)]
TPM.log <- log(TPM+1)
PCA <- prcomp(TPM.log, scale=TRUE)
PCA.mat <- as.data.frame(PCA$x)
PCA.PC1filt <- PCA.mat %>% filter(PC1 < quantile(PCA.mat[,"PC1"], .2)[[1]])
TPM3 <- TPM2[rowSums(TPM2)>1000,]
counts.sc <- t(TPM3)
dist <- dist(counts.sc)
clust <- hclust(dist, method="average")
dend <- as.dendrogram(clust)
par(mar=c(10,2,1,1))
my_colors <- ifelse(metadata$Subtype=="HGSC", "red",
ifelse(metadata$Subtype=="LGSC", "blue",
ifelse(metadata$Subtype=="OCCC", "yellow",
ifelse(metadata$Subtype=="EC", "green",
ifelse(metadata$Subtype=="SCCOHT","purple",
ifelse(metadata$Subtype=="MUC","orange","white"))))))
plot(dend)
colored_bars(colors = my_colors, dend = dend, rowLabels = "Subtype")

I tried a lot of different iterations of this (including: various
cutoffs for variance of genes, genes contributing most to first and
second principal components, more highly expressed genes, TPM vs vst
data, clustering methods), but the fundamental problem is that isogenic
pairs rarely cluster anywhere near each other, even though the PCA
analysis shows this relationship. I don’t know that this is a reliable
method for determining relatedness/subtyping, unless a robust gene set
is developed.
Data visualization
Plot Heatmap of top differentially expressed genes
vsd.df <-as.data.frame(assay(vsd))
pheatmap(vsd.df[rownames(res.filtered),], labels_col=colData(dds)[,c("CellLine")],annotation_col=df, color=colorRampPalette(c("white", "red"))(50))

Heatmap based on TPM
pheatmap(TPM.log[rownames(res.filtered),], labels_col=colData(dds)[,c("CellLine")], annotation_col=df, color=colorRampPalette(c("white", "red"))(10))

Heatmap with mean-centered data
center_scale <- function(x) {
scale(x, scale=FALSE)
}
vsd.meancenter <- apply(vsd.df, 1, center_scale)
vsd.meancenter <-t(vsd.meancenter)
colnames(vsd.meancenter) <- colnames(vsd.df)
vsd.meancenter <- as.data.frame(vsd.meancenter)
color <- colorRampPalette(brewer.pal(11, "PuOr"))(50)
myBreaks <- c(seq(min(vsd.meancenter[rownames(res.filtered),]), 0, length.out=ceiling(50/2) + 1),
seq(max(vsd.meancenter[rownames(res.filtered),])/50, max(vsd.meancenter[rownames(res.filtered),]), length.out=floor(50/2)))
pheatmap(vsd.meancenter[rownames(res.filtered),], labels_col=colData(dds)[,c("CellLine")], color=color, border_color = NA, annotation_col = df, breaks=myBreaks)

Volcano Plots
res.filtered %>%
ggplot(aes(x=log2FoldChange, y=-log10(pvalue), label=rownames(res.filtered))) +
geom_point() +
theme_minimal() +
scale_color_manual(values = c("black", "blue", "red"))+
geom_text_repel()

Run Gene Ontology
Re-run DESeq using IC50 as continuous variable
Create DESeqDataSet object dds
dds2 <- DESeqDataSetFromMatrix(countData = countmatrix2, colData = metadata, design = ~ Subtype + IC50)
converting counts to integer mode
Warning: some variables in design formula are characters, converting to factors the design formula contains one or more numeric variables that have mean or
standard deviation larger than 5 (an arbitrary threshold to trigger this message).
Including numeric variables with large mean can induce collinearity with the intercept.
Users should center and scale numeric variables in the design to improve GLM convergence.
Pre-filtering
This step removes genes with low expression to increase multiple
comparison power.
keep <- rowSums(counts(dds2)) >= 100
dds2 <- dds2[keep,]
nrow(dds2)
[1] 18308
Run DESeq2
dds2 <- DESeq(dds2)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
875 rows did not converge in beta, labelled in mcols(object)$betaConv. Use larger maxit argument with nbinomWaldTest
Print DESeq results
res2 <- results(dds2)
res2
log2 fold change (MLE): IC50
Wald test p-value: IC50
DataFrame with 18308 rows and 6 columns
baseMean log2FoldChange lfcSE stat pvalue padj
<numeric> <numeric> <numeric> <numeric> <numeric> <numeric>
A1BG 25.5152 -0.0250373 0.0747816 -0.334805 0.7377723 0.960709
A1BG-AS1 46.1094 -0.0105069 0.0701475 -0.149783 0.8809361 0.986400
A1CF 206.7023 0.0182140 0.1829988 0.099531 0.9207167 0.992385
A2M 1084.1287 -0.2270264 0.1656011 -1.370924 0.1703988 0.746062
A2M-AS1 16.4752 0.1649801 0.0765098 2.156327 0.0310582 0.488080
... ... ... ... ... ... ...
ZYG11A 420.769 0.00554376 0.0576535 0.0961566 0.9233962 0.992451
ZYG11B 2150.386 -0.01173472 0.0244272 -0.4803961 0.6309457 0.939287
ZYX 4329.425 -0.05132687 0.0304173 -1.6874224 0.0915222 0.650209
ZZEF1 1675.076 0.02185766 0.0240438 0.9090750 0.3633105 0.859931
ZZZ3 3280.365 0.02342910 0.0206913 1.1323150 0.2575020 0.810698
write.csv(as.data.frame(res2), file = "DESeq_IC50.csv")
Filter DESeq2 results for significant genes
Filter res for padj < 0.05 and |log2FC| >= 1.2
res.2.filtered <- as.data.frame(res2) %>%
filter(padj<0.05)%>%
filter(log2FoldChange >= 1.2 | log2FoldChange <= -1.2)
res.2.filtered
Data QC
PCA plot
pcaData2 <- plotPCA(vsd2, intgroup=c("Subtype", "PlatinumSensitivity","CellLine"), returnData=TRUE)
percentVar2 <- round(100 * attr(pcaData2, "percentVar"))
pca2 <- ggplot(pcaData2, aes(PC1, PC2, color=Subtype, shape=PlatinumSensitivity, label=CellLine)) +
geom_point(size=3) +
geom_text(hjust=0, vjust=0) +
xlab(paste0("PC1: ",percentVar2[1],"% variance")) +
ylab(paste0("PC2: ",percentVar2[2],"% variance")) +
coord_fixed() +
theme_classic() +
colScale
pca2
ggsave("pca_IC50.pdf", pca2)
Saving 7.29 x 4.51 in image

Data visualization
Plot Heatmap of top differentially expressed genes
vsd2.df <-as.data.frame(assay(vsd2))
color <- colorRampPalette(c("white", "red"))(40)
breaks <- seq(6,12,length.out=40)
pheatmap(vsd2.df[rownames(res.2.filtered),], labels_col=colData(dds2)[,c("CellLine")],annotation_col=df, color=color, breaks=breaks)

Heatmap with mean-centered data
vsd2.meancenter <- apply(vsd2.df, 1, center_scale)
vsd2.meancenter <-t(vsd2.meancenter)
colnames(vsd2.meancenter) <- colnames(vsd2.df)
vsd2.meancenter <- as.data.frame(vsd2.meancenter)
color <- colorRampPalette(brewer.pal(11, "PuOr"))(50)
myBreaks2 <- c(seq(min(vsd2.meancenter[rownames(res.2.filtered),]), 0, length.out=ceiling(50/2) + 1),
seq(max(vsd2.meancenter[rownames(res.2.filtered),])/50, max(vsd2.meancenter[rownames(res.2.filtered),]), length.out=floor(50/2)))
pheatmap(vsd2.meancenter[rownames(res.2.filtered),], labels_col=colData(dds2)[,c("CellLine")], color=color, border_color = NA, annotation_col = df, breaks=myBreaks2)

Volcano Plots
res.2.filtered %>%
ggplot(aes(x=log2FoldChange, y=-log10(pvalue), label=rownames(res.2.filtered))) +
geom_point() +
theme_minimal() +
scale_color_manual(values = c("black", "blue", "red"))+
geom_text_repel()

Heatmap of genes involved in cisplatin resistance
Plot heatmap of genes annotated as involved in cisplatin resistance
in PMID: 34645978
resist.genes <- read.delim("ResistanceGenes.txt", header = FALSE)
resist.genes <- resist.genes$V1
pheatmap(TPM.log[resist.genes[resist.genes %in% row.names(TPM.log)],], labels_col=colData(dds)[,c("CellLine")],annotation_col=df, color=colorRampPalette(c("white", "red"))(50))

res2.resist <- res2[resist.genes[resist.genes %in% row.names(res2)],c("log2FoldChange","padj")]
res2.resist <- as.data.frame(res2.resist) %>%
filter(padj < 0.05)
res2.resist
Heatmap with mean-centered data
myBreaks3 <- c(seq(min(vsd.meancenter[resist.genes[resist.genes %in% row.names(vsd.meancenter)],]), 0, length.out=ceiling(50/2) + 1),
seq(max(vsd.meancenter[resist.genes[resist.genes %in% row.names(vsd.meancenter)],])/50, max(vsd.meancenter[resist.genes[resist.genes %in% row.names(vsd.meancenter)],]), length.out=floor(50/2)))
pheatmap(vsd.meancenter[resist.genes[resist.genes %in% row.names(vsd.meancenter)],], labels_col=colData(dds)[,c("CellLine")],annotation_col=df, color=color, border_color = NA, breaks=myBreaks3)

LS0tCnRpdGxlOiAiT3ZhcmlhbiBDYW5jZXIgQ2VsbCBMaW5lIFJOQXNlcSBQbGF0aW51bSBTZW5zaXRpdml0eSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMgSW50cm9kdWN0aW9uICAKClJOQS1zZXEgd2FzIHJ1biBvbiAzNiBvdmFyaWFuIGNhbmNlciBjZWxsIGxpbmVzLCBlYWNoIGluIHNpbmdsaWNhdGUuICAKCkFsbCAzNiBjZWxsIGxpbmVzIGhhdmUgNzJoIGNpc3BsYXRpbiBJQzUwcyBkZXRlcm1pbmVkIGJ5IEtyaXN0aW4gQWRhbXMgYW5kIEtlbmRyYSAKV2VuZHQgaW4gdGhlIExhbmcgTGFiLiAKCiFbQ2lzcGxhdGluIElDNTBzXShDaXNwbGF0aW5JQzUwLmpwZyl7aGVpZ2h0PTUwJSwgd2lkdGg9NTAlfQoKQWRkIGluIGxpbmtzIHRvIExhYiBub3RlYm9va3MgZm9yIElDNTAsIFJOQXNlcSBzYW1wbGUgcHJlcAoKIyMgSW5wdXRzCgpJbnB1dHMgY29uc2lzdGVkIG9mOiAgCgoqIE1ldGFkYXRhIHNwcmVhZHNoZWV0ICAKKiBzYWxtb24ubWVyZ2VkLmdlbmVfY291bnRzLnRzdiBmaWxlIGZyb20gbmYtY29yZS9ybmFzZXEgcGlwZWxpbmUgb3V0cHV0ICAKKiBOZWNlc3NhcnkgcGFja2FnZXMgIAoKYGBge3IgbG9hZCBwYWNrYWdlcywgaW5jbHVkZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeSh2c24pCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoZGVuZGV4dGVuZCkKYGBgCgojIyBERVNlcTIKClRvIGRldGVybWluZSBnZW5lcyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYmV0d2VlbiBjaXNwbGF0aW4gc2Vuc2l0aXZlIGFuZCByZXNpc3RhbnQgY2VsbCBsaW5lcywgd2UgdXNlZCB0aGUgbWVkaWFuIGNpc3BsYXRpbiBJQzUwIG9mIGFsbCAzNiBjZWxsIGxpbmVzIGFzIGEgY3V0LXBvaW50LCBhbmQgZXhjbHVkZWQgY2VsbCBsaW5lcyB3aXRoaW4gKy8tIG9uZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIG1lZGlhbi4gVGhlc2Ugd2VyZSBkZWZpbmVkIGluIHRoZSBtZXRhZGF0YSB0YWJsZS4KCiMjIyBSZWFkIGluIG1ldGFkYXRhIHRhYmxlCgpgYGB7ciBsb2FkIG1ldGF0YWJsZX0KYXMuZGF0YS5mcmFtZShyZWFkX2V4Y2VsKCJNZXRhZGF0YTMueGxzeCIpKSAtPiBtZXRhZGF0YQpyb3cubmFtZXMobWV0YWRhdGEpIDwtIG1ldGFkYXRhJGZpbGVzCm1ldGFkYXRhCmBgYAojIyMgTG9hZCBjb3VudCBtYXRyaXgKCmBgYHtyIHJlYWQgY291bnQgbWF0cml4fQpjb3VudG1hdHJpeCA8LSBhcy5tYXRyaXgocmVhZC5kZWxpbSgiLi4vc3Rhcl9zYWxtb24vc2FsbW9uLm1lcmdlZC5nZW5lX2NvdW50cy50c3YiLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPSJnZW5lX2lkIikpCmNvdW50bWF0cml4IDwtIGNvdW50bWF0cml4WywtMV0KY291bnRtYXRyaXgyIDwtIG1hdHJpeChhcy5udW1lcmljKGNvdW50bWF0cml4KSwgbmNvbCA9IG5jb2woY291bnRtYXRyaXgpLCBkaW1uYW1lcyA9IGxpc3Qocm93bmFtZXMoY291bnRtYXRyaXgpLCBjb2xuYW1lcyhjb3VudG1hdHJpeCkpKQpjb3VudG1hdHJpeDIgPC0gcm91bmQoY291bnRtYXRyaXgyKQpjb3VudG1hdHJpeDIgPC0gY291bnRtYXRyaXgyWyxyb3duYW1lcyhtZXRhZGF0YSldCmhlYWQoY291bnRtYXRyaXgyKQpgYGAKIyMjIENyZWF0ZSBERVNlcURhdGFTZXQgb2JqZWN0IGRkcwoKYGBge3IgREVTZXFEYXRhU2V0IGdlbmVyYXRpb259CmRkcyA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50RGF0YSA9IGNvdW50bWF0cml4MiwgY29sRGF0YSA9IG1ldGFkYXRhLCBkZXNpZ24gPSB+IFN1YnR5cGUgKyBQbGF0aW51bVNlbnNpdGl2aXR5KQpgYGAKIyMjIFByZS1maWx0ZXJpbmcKVGhpcyBzdGVwIHJlbW92ZXMgZ2VuZXMgd2l0aCBsb3cgZXhwcmVzc2lvbiB0byBpbmNyZWFzZSBtdWx0aXBsZSBjb21wYXJpc29uIHBvd2VyLgoKYGBge3J9CmtlZXAgPC0gcm93U3Vtcyhjb3VudHMoZGRzKSkgPj0gNTAwCmRkcyA8LSBkZHNba2VlcCxdCm5yb3coZGRzKQpgYGAKCgojIyMgUnVuIERFU2VxMiAKYGBge3IgREVTZXEyfQpkZHMgPC0gREVTZXEoZGRzKQpgYGAKCiMjIyBQcmludCBERVNlcSByZXN1bHRzCmBgYHtyIGNyZWF0ZSByZXN1bHRzIHRhYmxlfQpyZXMgPC0gcmVzdWx0cyhkZHMsIGNvbnRyYXN0PWMoIlBsYXRpbnVtU2Vuc2l0aXZpdHkiLCAicmVzaXN0YW50IiwgInNlbnNpdGl2ZSIpKQpyZXMKd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUocmVzKSwgZmlsZSA9ICJERVNlcS5jc3YiKQpgYGAKCiMjIyBGaWx0ZXIgREVTZXEyIHJlc3VsdHMgZm9yIHNpZ25pZmljYW50IGdlbmVzCkZpbHRlciByZXMgZm9yIHBhZGogPCAwLjA1IGFuZCB8bG9nMkZDfCA+PSAxLjIKYGBge3IgZmlsdGVyIHJlc30KcmVzLmZpbHRlcmVkIDwtIGFzLmRhdGEuZnJhbWUocmVzKSAlPiUKICBmaWx0ZXIocGFkajwwLjA1KSU+JQogIGZpbHRlcihsb2cyRm9sZENoYW5nZSA+PSAxLjIgfCBsb2cyRm9sZENoYW5nZSA8PSAtMS4yKQpyZXMuZmlsdGVyZWQKYGBgCiMjIERhdGEgUUMKCiMjIyBEYXRhIHRyYW5zZm9ybWF0aW9uIApIZXJlIHdlIHBlcmZvcm1lZCBub3JtYWwgdHJhbnNmb3JtYXRpb24gW2xvZzIobisxKV0sIHZhcmlhbmNlIHN0YWJpbGl6ZWQgdHJhbnNmb3JtYXRpb24sIGFuZCByZWd1bGFyaXplZCBsb2cgdHJhbmZvcm1hdGlvbiB0byBpbXByb3ZlIHZpc3VhbGl6YXRpb24gb2YgdGhlIGRhdGEgdmFsdWVzLiBUbyBzcGVlZCB1cCBzdWJzZXF1ZW50IHJlLXJ1bnMsIHdlIGhhdmUgaGlkZGVuIGFuYWx5c2lzIGZvciBub24tdnN0LgoKYGBge3Igbm9ybSB0cmFuc2Zvcm1hdGlvbiBhbmQgc3RkZXYgcGxvdH0KIyBudGQgPC0gbm9ybVRyYW5zZm9ybShkZHMpCiNtZWFuU2RQbG90KGFzc2F5KG50ZCkpCmBgYAoKCmBgYHtyIHZhciBzdGFiIHRyYW5zZm9ybWF0aW9uIGFuZCBzdGRldiBwbG90fQp2c2QgPC0gdnN0KGRkcykKbWVhblNkUGxvdChhc3NheSh2c2QpKQpgYGAKCgpgYGB7ciByZWcgbG9nIHRyYW5zZm9ybWF0aW9uIGFuZCBzdGRldiBwbG90fQojIHJsZCA8LSBybG9nKGRkcykKIyBtZWFuU2RQbG90KGFzc2F5KHJsZCkpCmBgYAoKQmFzZWQgb24gdGhpcyBkYXRhLCB2YXJpYW5jZS1zdGFiaWxpemVkIHRyYW5mb3JtYXRpb24gbGVhZCB0byB0aGUgbG93ZXN0IHN0YW5kYXJkIGRldmlhdGlvbiBiZXR3ZWVuIHNhbXBsZXMgYW5kIHdhcyBtb3N0bHkgbG9jYXRlZCB0b3dhcmRzIHRoZSBoaWdoIGV4cHJlc3Npb24gdHJhbnNjcmlwdHMsIGFzIG1pZ2h0IGJlIGV4cGVjdGVkLgoKIyMjIFBDQSBwbG90CmBgYHtyIHZzZCBQQ0F9CnBjYURhdGEgPC0gcGxvdFBDQSh2c2QsIGludGdyb3VwPWMoIlN1YnR5cGUiLCAiUGxhdGludW1TZW5zaXRpdml0eSIsIkNlbGxMaW5lIiksIHJldHVybkRhdGE9VFJVRSkKcGVyY2VudFZhciA8LSByb3VuZCgxMDAgKiBhdHRyKHBjYURhdGEsICJwZXJjZW50VmFyIikpCm15Q29sb3JzIDwtIGMoIiM3NkFCN0UiLCAiIzYzRTY3OCIsICIjMUQzMkZCIiwgIiM3Qjg3RkQiLCAiIzAxQkQxRiIsICIjRThBNDI2IiwgIiMwQjdDMUQiLCAiI0JCMTlFNyIpCm5hbWVzKG15Q29sb3JzKSA8LSBsZXZlbHMocGNhRGF0YSRTdWJ0eXBlKQpjb2xTY2FsZSA8LSBzY2FsZV9jb2xvdXJfbWFudWFsKG5hbWUgPSAiU3VidHlwZSIsdmFsdWVzID0gbXlDb2xvcnMpCnBjYSA8LSBnZ3Bsb3QocGNhRGF0YSwgYWVzKFBDMSwgUEMyLCBjb2xvcj1TdWJ0eXBlLCBzaGFwZT1QbGF0aW51bVNlbnNpdGl2aXR5LCBsYWJlbD1DZWxsTGluZSkpICsKICBnZW9tX3BvaW50KHNpemU9MykgKwogIGdlb21fdGV4dChoanVzdD0wLCB2anVzdD0wKSArCiAgeGxhYihwYXN0ZTAoIlBDMTogIixwZXJjZW50VmFyWzFdLCIlIHZhcmlhbmNlIikpICsKICB5bGFiKHBhc3RlMCgiUEMyOiAiLHBlcmNlbnRWYXJbMl0sIiUgdmFyaWFuY2UiKSkgKwogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIGNvbFNjYWxlCnBjYQpnZ3NhdmUoInBjYS5wZGYiLCBwY2EpCmBgYApQQ0EgcGxvdCBncm91cHMgc2FtcGxlcyByb3VnaGx5IGJ5IHN1YnR5cGUKCiMjIyBTYW1wbGUtd2lzZSBjb3JyZWxhdGlvbgoKYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPSA4fQp2c2QubW9kIDwtIHZzZC5kZiAKY29sbmFtZXModnNkLm1vZCkgPC0gbWV0YWRhdGEkQ2VsbExpbmVbbWF0Y2goY29sbmFtZXModnNkLm1vZCksIG1ldGFkYXRhJGZpbGVzKV0KdnNkLm1vZCRnZW5lIDwtIHJvdy5uYW1lcyh2c2QubW9kKQoKY29yciA8LSB2c2QubW9kICU+JQogIHNlbGVjdCgtZ2VuZSkgJT4lCiAgY29yKG1ldGhvZCA9ICJzcGVhcm1hbiIpCgpkZi5tb2QgPC0gZGYKcm93Lm5hbWVzKGRmLm1vZCkgPC0gbWV0YWRhdGEkQ2VsbExpbmVbbWF0Y2gocm93Lm5hbWVzKGRmLm1vZCksIG1ldGFkYXRhJGZpbGVzKV0KCnBoZWF0bWFwKGNvcnIsIGFubm90YXRpb25fY29sPWRmLm1vZCkKYGBgCgojIyMgQmFybmVzIFJGIGFwcHJvYWNoPwpDb250YWN0ZWQgUEkgMTAvMjcvMjAyMiBiZWNhdXNlIHNjcmlwdCBpcyBub3Qgb24gZ2l0aHViLiBObyByZXNwb25zZSBhcyBvZiAxMi8xNi8yMDIyLgoKCiMjIyBQbG90IGhlYXRtYXBzIHRvIGNoZWNrIHNhbXBsZSB0byBzYW1wbGUgdmFyaWFiaWxpdHkKUGxvdHRpbmcgdGhlIHRvcCAxMDAgbW9zdCBoaWdobHkgZXhwcmVzc2VkIGdlbmVzOgpgYGB7ciBoZWF0bWFwc30Kc2VsZWN0IDwtIG9yZGVyKHJvd01lYW5zKGNvdW50cyhkZHMsbm9ybWFsaXplZD1UUlVFKSksCiAgICAgICAgICAgICAgICBkZWNyZWFzaW5nPVRSVUUpWzE6MTAwXQpkZiA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEoZGRzKVssYygiU3VidHlwZSIsICJQbGF0aW51bVNlbnNpdGl2aXR5IildKQpwaGVhdG1hcChhc3NheSh2c2QpW3NlbGVjdCxdLCBjbHVzdGVyX3Jvd3M9RkFMU0UsIHNob3dfcm93bmFtZXM9RkFMU0UsCiAgICAgICAgIGxhYmVsc19jb2w9Y29sRGF0YShkZHMpWyxjKCJDZWxsTGluZSIpXSxhbm5vdGF0aW9uX2NvbD1kZikKYGBgCiMjIyBEZW5kcm9ncmFtIGJhc2VkIG9uIGdlbmUgZXhwcmVzc2lvbgpQdWxsIGdlbmVzIGNvbnRyaWJ1dGluZyB0byBwcmluY2lwYWwgY29tcG9uZW50cyAxICYgMgpgYGB7cn0KVFBNIDwtIGFzLm1hdHJpeChyZWFkLmRlbGltKCIuLi9zdGFyX3NhbG1vbi9zYWxtb24ubWVyZ2VkLmdlbmVfdHBtLnRzdiIsIHNlcD0iXHQiLCByb3cubmFtZXM9ImdlbmVfaWQiKSkKVFBNIDwtIFRQTVssLTFdClRQTSA8LSBtYXRyaXgoYXMubnVtZXJpYyhUUE0pLCBuY29sID0gbmNvbChUUE0pLCBkaW1uYW1lcyA9IGxpc3Qocm93bmFtZXMoVFBNKSwgY29sbmFtZXMoVFBNKSkpClRQTSA8LSByb3VuZChUUE0pClRQTSA8LSBUUE1bLHJvd25hbWVzKG1ldGFkYXRhKV0KVFBNMiA8LSBUUE0KY29sbmFtZXMoVFBNMikgPC0gbWV0YWRhdGEkQ2VsbExpbmVbbWF0Y2goY29sbmFtZXMoVFBNMiksIG1ldGFkYXRhJGZpbGVzKV0KVFBNLmxvZyA8LSBsb2coVFBNKzEpClBDQSA8LSBwcmNvbXAoVFBNLmxvZywgc2NhbGU9VFJVRSkKUENBLm1hdCA8LSBhcy5kYXRhLmZyYW1lKFBDQSR4KQpQQ0EuUEMxZmlsdCA8LSBQQ0EubWF0ICU+JSBmaWx0ZXIoUEMxIDwgcXVhbnRpbGUoUENBLm1hdFssIlBDMSJdLCAuMilbWzFdXSkKYGBgCgoKYGBge3J9ClRQTTMgPC0gVFBNMltyb3dTdW1zKFRQTTIpPjEwMDAsXQpjb3VudHMuc2MgPC0gdChUUE0zKQpkaXN0IDwtIGRpc3QoY291bnRzLnNjKQpjbHVzdCA8LSBoY2x1c3QoZGlzdCwgbWV0aG9kPSJhdmVyYWdlIikKZGVuZCA8LSBhcy5kZW5kcm9ncmFtKGNsdXN0KQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTB9CnBhcihtYXI9YygxMCwyLDEsMSkpCm15X2NvbG9ycyA8LSBpZmVsc2UobWV0YWRhdGEkU3VidHlwZT09IkhHU0MiLCAicmVkIiwgCiAgICAgICAgICAgICAgICAgICAgaWZlbHNlKG1ldGFkYXRhJFN1YnR5cGU9PSJMR1NDIiwgImJsdWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKG1ldGFkYXRhJFN1YnR5cGU9PSJPQ0NDIiwgInllbGxvdyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKG1ldGFkYXRhJFN1YnR5cGU9PSJFQyIsICJncmVlbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKG1ldGFkYXRhJFN1YnR5cGU9PSJTQ0NPSFQiLCJwdXJwbGUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKG1ldGFkYXRhJFN1YnR5cGU9PSJNVUMiLCJvcmFuZ2UiLCJ3aGl0ZSIpKSkpKSkKcGxvdChkZW5kKQpjb2xvcmVkX2JhcnMoY29sb3JzID0gbXlfY29sb3JzLCBkZW5kID0gZGVuZCwgcm93TGFiZWxzID0gIlN1YnR5cGUiKQpgYGAKSSB0cmllZCBhIGxvdCBvZiBkaWZmZXJlbnQgaXRlcmF0aW9ucyBvZiB0aGlzIChpbmNsdWRpbmc6IHZhcmlvdXMgY3V0b2ZmcyBmb3IgdmFyaWFuY2Ugb2YgZ2VuZXMsIGdlbmVzIGNvbnRyaWJ1dGluZyBtb3N0IHRvIGZpcnN0IGFuZCBzZWNvbmQgcHJpbmNpcGFsIGNvbXBvbmVudHMsIG1vcmUgaGlnaGx5IGV4cHJlc3NlZCBnZW5lcywgVFBNIHZzIHZzdCBkYXRhLCBjbHVzdGVyaW5nIG1ldGhvZHMpLCBidXQgdGhlIGZ1bmRhbWVudGFsIHByb2JsZW0gaXMgdGhhdCBpc29nZW5pYyBwYWlycyByYXJlbHkgY2x1c3RlciBhbnl3aGVyZSBuZWFyIGVhY2ggb3RoZXIsIGV2ZW4gdGhvdWdoIHRoZSBQQ0EgYW5hbHlzaXMgc2hvd3MgdGhpcyByZWxhdGlvbnNoaXAuIEkgZG9uJ3Qga25vdyB0aGF0IHRoaXMgaXMgYSByZWxpYWJsZSBtZXRob2QgZm9yIGRldGVybWluaW5nIHJlbGF0ZWRuZXNzL3N1YnR5cGluZywgdW5sZXNzIGEgcm9idXN0IGdlbmUgc2V0IGlzIGRldmVsb3BlZC4gCgojIyBEYXRhIHZpc3VhbGl6YXRpb24KCiMjIyBQbG90IEhlYXRtYXAgb2YgdG9wIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcwpgYGB7ciBwbG90IGhlYXRtYXAsIGZpZy5oZWlnaHQgPSA4LCBmaWcud2lkdGggPSA4fQp2c2QuZGYgPC1hcy5kYXRhLmZyYW1lKGFzc2F5KHZzZCkpCnBoZWF0bWFwKHZzZC5kZltyb3duYW1lcyhyZXMuZmlsdGVyZWQpLF0sIGxhYmVsc19jb2w9Y29sRGF0YShkZHMpWyxjKCJDZWxsTGluZSIpXSxhbm5vdGF0aW9uX2NvbD1kZiwgY29sb3I9Y29sb3JSYW1wUGFsZXR0ZShjKCJ3aGl0ZSIsICJyZWQiKSkoNTApKQpgYGAKIyMjIEhlYXRtYXAgYmFzZWQgb24gVFBNCmBgYHtyLCBmaWcuaGVpZ2h0ID0gOCwgZmlnLndpZHRoID0gOH0KcGhlYXRtYXAoVFBNLmxvZ1tyb3duYW1lcyhyZXMuZmlsdGVyZWQpLF0sIGxhYmVsc19jb2w9Y29sRGF0YShkZHMpWyxjKCJDZWxsTGluZSIpXSwgYW5ub3RhdGlvbl9jb2w9ZGYsIGNvbG9yPWNvbG9yUmFtcFBhbGV0dGUoYygid2hpdGUiLCAicmVkIikpKDEwKSkKYGBgCiMjIyBIZWF0bWFwIHdpdGggbWVhbi1jZW50ZXJlZCBkYXRhCmBgYHtyLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD04fQpjZW50ZXJfc2NhbGUgPC0gZnVuY3Rpb24oeCkgewogIHNjYWxlKHgsIHNjYWxlPUZBTFNFKQp9CnZzZC5tZWFuY2VudGVyIDwtIGFwcGx5KHZzZC5kZiwgMSwgY2VudGVyX3NjYWxlKQp2c2QubWVhbmNlbnRlciA8LXQodnNkLm1lYW5jZW50ZXIpCmNvbG5hbWVzKHZzZC5tZWFuY2VudGVyKSA8LSBjb2xuYW1lcyh2c2QuZGYpCnZzZC5tZWFuY2VudGVyIDwtIGFzLmRhdGEuZnJhbWUodnNkLm1lYW5jZW50ZXIpIApjb2xvciA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoMTEsICJQdU9yIikpKDUwKQpteUJyZWFrcyA8LSBjKHNlcShtaW4odnNkLm1lYW5jZW50ZXJbcm93bmFtZXMocmVzLmZpbHRlcmVkKSxdKSwgMCwgbGVuZ3RoLm91dD1jZWlsaW5nKDUwLzIpICsgMSksIAogICAgICAgICAgICAgIHNlcShtYXgodnNkLm1lYW5jZW50ZXJbcm93bmFtZXMocmVzLmZpbHRlcmVkKSxdKS81MCwgbWF4KHZzZC5tZWFuY2VudGVyW3Jvd25hbWVzKHJlcy5maWx0ZXJlZCksXSksIGxlbmd0aC5vdXQ9Zmxvb3IoNTAvMikpKQpwaGVhdG1hcCh2c2QubWVhbmNlbnRlcltyb3duYW1lcyhyZXMuZmlsdGVyZWQpLF0sIGxhYmVsc19jb2w9Y29sRGF0YShkZHMpWyxjKCJDZWxsTGluZSIpXSwgY29sb3I9Y29sb3IsIGJvcmRlcl9jb2xvciA9IE5BLCBhbm5vdGF0aW9uX2NvbCA9IGRmLCBicmVha3M9bXlCcmVha3MpCmBgYAoKIyMjIFZvbGNhbm8gUGxvdHMKYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEwfQpyZXMuZmlsdGVyZWQgJT4lIAogIGdncGxvdChhZXMoeD1sb2cyRm9sZENoYW5nZSwgeT0tbG9nMTAocHZhbHVlKSwgbGFiZWw9cm93bmFtZXMocmVzLmZpbHRlcmVkKSkpICsKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwgImJsdWUiLCAicmVkIikpKwogIGdlb21fdGV4dF9yZXBlbCgpCmBgYAogIAojIyMgUnVuIEdlbmUgT250b2xvZ3kKYGBge3IgcnVuIGdlbmUgb250b2xvZ3l9CgpgYGAKCiMjIFJlLXJ1biBERVNlcSB1c2luZyBJQzUwIGFzIGNvbnRpbnVvdXMgdmFyaWFibGUKIyMjIENyZWF0ZSBERVNlcURhdGFTZXQgb2JqZWN0IGRkcwoKYGBge3IgfQpkZHMyIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhID0gY291bnRtYXRyaXgyLCBjb2xEYXRhID0gbWV0YWRhdGEsIGRlc2lnbiA9IH4gU3VidHlwZSArIElDNTApCmBgYAojIyMgUHJlLWZpbHRlcmluZwpUaGlzIHN0ZXAgcmVtb3ZlcyBnZW5lcyB3aXRoIGxvdyBleHByZXNzaW9uIHRvIGluY3JlYXNlIG11bHRpcGxlIGNvbXBhcmlzb24gcG93ZXIuCgpgYGB7cn0Ka2VlcCA8LSByb3dTdW1zKGNvdW50cyhkZHMyKSkgPj0gMTAwCmRkczIgPC0gZGRzMltrZWVwLF0KbnJvdyhkZHMyKQpgYGAKCgojIyMgUnVuIERFU2VxMiAKYGBge3IgfQpkZHMyIDwtIERFU2VxKGRkczIpCmBgYAoKIyMjIFByaW50IERFU2VxIHJlc3VsdHMKYGBge3IgfQpyZXMyIDwtIHJlc3VsdHMoZGRzMikKcmVzMgp3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShyZXMyKSwgZmlsZSA9ICJERVNlcV9JQzUwLmNzdiIpCmBgYAoKIyMjIEZpbHRlciBERVNlcTIgcmVzdWx0cyBmb3Igc2lnbmlmaWNhbnQgZ2VuZXMKRmlsdGVyIHJlcyBmb3IgcGFkaiA8IDAuMDUgYW5kIHxsb2cyRkN8ID49IDEuMgpgYGB7ciB9CnJlcy4yLmZpbHRlcmVkIDwtIGFzLmRhdGEuZnJhbWUocmVzMikgJT4lCiAgZmlsdGVyKHBhZGo8MC4wNSklPiUKICBmaWx0ZXIobG9nMkZvbGRDaGFuZ2UgPj0gMS4yIHwgbG9nMkZvbGRDaGFuZ2UgPD0gLTEuMikKcmVzLjIuZmlsdGVyZWQKYGBgCgojIyBEYXRhIFFDCgojIyMgRGF0YSB0cmFuc2Zvcm1hdGlvbiAKCmBgYHtyIH0KdnNkMiA8LSB2c3QoZGRzMikKbWVhblNkUGxvdChhc3NheSh2c2QyKSkKYGBgCgojIyMgUENBIHBsb3QKYGBge3J9CnBjYURhdGEyIDwtIHBsb3RQQ0EodnNkMiwgaW50Z3JvdXA9YygiU3VidHlwZSIsICJQbGF0aW51bVNlbnNpdGl2aXR5IiwiQ2VsbExpbmUiKSwgcmV0dXJuRGF0YT1UUlVFKQpwZXJjZW50VmFyMiA8LSByb3VuZCgxMDAgKiBhdHRyKHBjYURhdGEyLCAicGVyY2VudFZhciIpKQpwY2EyIDwtIGdncGxvdChwY2FEYXRhMiwgYWVzKFBDMSwgUEMyLCBjb2xvcj1TdWJ0eXBlLCBzaGFwZT1QbGF0aW51bVNlbnNpdGl2aXR5LCBsYWJlbD1DZWxsTGluZSkpICsKICBnZW9tX3BvaW50KHNpemU9MykgKwogIGdlb21fdGV4dChoanVzdD0wLCB2anVzdD0wKSArCiAgeGxhYihwYXN0ZTAoIlBDMTogIixwZXJjZW50VmFyMlsxXSwiJSB2YXJpYW5jZSIpKSArCiAgeWxhYihwYXN0ZTAoIlBDMjogIixwZXJjZW50VmFyMlsyXSwiJSB2YXJpYW5jZSIpKSArCiAgY29vcmRfZml4ZWQoKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBjb2xTY2FsZQpwY2EyCmdnc2F2ZSgicGNhX0lDNTAucGRmIiwgcGNhMikKYGBgCgojIyBEYXRhIHZpc3VhbGl6YXRpb24KCiMjIyBQbG90IEhlYXRtYXAgb2YgdG9wIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcwpgYGB7ciAsIGZpZy5oZWlnaHQgPSAzLCBmaWcud2lkdGggPSA4fQp2c2QyLmRmIDwtYXMuZGF0YS5mcmFtZShhc3NheSh2c2QyKSkKY29sb3IgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJ3aGl0ZSIsICJyZWQiKSkoNDApCmJyZWFrcyA8LSBzZXEoNiwxMixsZW5ndGgub3V0PTQwKSAgCnBoZWF0bWFwKHZzZDIuZGZbcm93bmFtZXMocmVzLjIuZmlsdGVyZWQpLF0sIGxhYmVsc19jb2w9Y29sRGF0YShkZHMyKVssYygiQ2VsbExpbmUiKV0sYW5ub3RhdGlvbl9jb2w9ZGYsIGNvbG9yPWNvbG9yLCBicmVha3M9YnJlYWtzKQpgYGAKCiMjIyBIZWF0bWFwIHdpdGggbWVhbi1jZW50ZXJlZCBkYXRhCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD04fQp2c2QyLm1lYW5jZW50ZXIgPC0gYXBwbHkodnNkMi5kZiwgMSwgY2VudGVyX3NjYWxlKQp2c2QyLm1lYW5jZW50ZXIgPC10KHZzZDIubWVhbmNlbnRlcikKY29sbmFtZXModnNkMi5tZWFuY2VudGVyKSA8LSBjb2xuYW1lcyh2c2QyLmRmKQp2c2QyLm1lYW5jZW50ZXIgPC0gYXMuZGF0YS5mcmFtZSh2c2QyLm1lYW5jZW50ZXIpIApjb2xvciA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoMTEsICJQdU9yIikpKDUwKQpteUJyZWFrczIgPC0gYyhzZXEobWluKHZzZDIubWVhbmNlbnRlcltyb3duYW1lcyhyZXMuMi5maWx0ZXJlZCksXSksIDAsIGxlbmd0aC5vdXQ9Y2VpbGluZyg1MC8yKSArIDEpLCAKICAgICAgICAgICAgICBzZXEobWF4KHZzZDIubWVhbmNlbnRlcltyb3duYW1lcyhyZXMuMi5maWx0ZXJlZCksXSkvNTAsIG1heCh2c2QyLm1lYW5jZW50ZXJbcm93bmFtZXMocmVzLjIuZmlsdGVyZWQpLF0pLCBsZW5ndGgub3V0PWZsb29yKDUwLzIpKSkKcGhlYXRtYXAodnNkMi5tZWFuY2VudGVyW3Jvd25hbWVzKHJlcy4yLmZpbHRlcmVkKSxdLCBsYWJlbHNfY29sPWNvbERhdGEoZGRzMilbLGMoIkNlbGxMaW5lIildLCBjb2xvcj1jb2xvciwgYm9yZGVyX2NvbG9yID0gTkEsIGFubm90YXRpb25fY29sID0gZGYsIGJyZWFrcz1teUJyZWFrczIpCmBgYAoKIyMjIFZvbGNhbm8gUGxvdHMKYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEwfQpyZXMuMi5maWx0ZXJlZCAlPiUgCiAgZ2dwbG90KGFlcyh4PWxvZzJGb2xkQ2hhbmdlLCB5PS1sb2cxMChwdmFsdWUpLCBsYWJlbD1yb3duYW1lcyhyZXMuMi5maWx0ZXJlZCkpKSArCiAgZ2VvbV9wb2ludCgpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIsICJibHVlIiwgInJlZCIpKSsKICBnZW9tX3RleHRfcmVwZWwoKQpgYGAKCgoKCgoKIyMjIEhlYXRtYXAgb2YgZ2VuZXMgaW52b2x2ZWQgaW4gY2lzcGxhdGluIHJlc2lzdGFuY2UKUGxvdCBoZWF0bWFwIG9mIGdlbmVzIGFubm90YXRlZCBhcyBpbnZvbHZlZCBpbiBjaXNwbGF0aW4gcmVzaXN0YW5jZSBpbiBQTUlEOiAzNDY0NTk3OApgYGB7ciwgZmlnLmhlaWdodCA9IDQ1LCBmaWcud2lkdGggPSA4fQpyZXNpc3QuZ2VuZXMgPC0gcmVhZC5kZWxpbSgiUmVzaXN0YW5jZUdlbmVzLnR4dCIsIGhlYWRlciA9IEZBTFNFKQpyZXNpc3QuZ2VuZXMgPC0gcmVzaXN0LmdlbmVzJFYxCnBoZWF0bWFwKFRQTS5sb2dbcmVzaXN0LmdlbmVzW3Jlc2lzdC5nZW5lcyAlaW4lIHJvdy5uYW1lcyhUUE0ubG9nKV0sXSwgbGFiZWxzX2NvbD1jb2xEYXRhKGRkcylbLGMoIkNlbGxMaW5lIildLGFubm90YXRpb25fY29sPWRmLCBjb2xvcj1jb2xvclJhbXBQYWxldHRlKGMoIndoaXRlIiwgInJlZCIpKSg1MCkpCmBgYAoKYGBge3J9CnJlczIucmVzaXN0IDwtIHJlczJbcmVzaXN0LmdlbmVzW3Jlc2lzdC5nZW5lcyAlaW4lIHJvdy5uYW1lcyhyZXMyKV0sYygibG9nMkZvbGRDaGFuZ2UiLCJwYWRqIildCnJlczIucmVzaXN0IDwtIGFzLmRhdGEuZnJhbWUocmVzMi5yZXNpc3QpICU+JQogIGZpbHRlcihwYWRqIDwgMC4wNSkKcmVzMi5yZXNpc3QKYGBgCiMjIyBIZWF0bWFwIHdpdGggbWVhbi1jZW50ZXJlZCBkYXRhCmBgYHtyLCBmaWcuaGVpZ2h0PTQ1LCBmaWcud2lkdGg9OH0KbXlCcmVha3MzIDwtIGMoc2VxKG1pbih2c2QubWVhbmNlbnRlcltyZXNpc3QuZ2VuZXNbcmVzaXN0LmdlbmVzICVpbiUgcm93Lm5hbWVzKHZzZC5tZWFuY2VudGVyKV0sXSksIDAsIGxlbmd0aC5vdXQ9Y2VpbGluZyg1MC8yKSArIDEpLCAKICAgICAgICAgICAgICBzZXEobWF4KHZzZC5tZWFuY2VudGVyW3Jlc2lzdC5nZW5lc1tyZXNpc3QuZ2VuZXMgJWluJSByb3cubmFtZXModnNkLm1lYW5jZW50ZXIpXSxdKS81MCwgbWF4KHZzZC5tZWFuY2VudGVyW3Jlc2lzdC5nZW5lc1tyZXNpc3QuZ2VuZXMgJWluJSByb3cubmFtZXModnNkLm1lYW5jZW50ZXIpXSxdKSwgbGVuZ3RoLm91dD1mbG9vcig1MC8yKSkpCnBoZWF0bWFwKHZzZC5tZWFuY2VudGVyW3Jlc2lzdC5nZW5lc1tyZXNpc3QuZ2VuZXMgJWluJSByb3cubmFtZXModnNkLm1lYW5jZW50ZXIpXSxdLCBsYWJlbHNfY29sPWNvbERhdGEoZGRzKVssYygiQ2VsbExpbmUiKV0sYW5ub3RhdGlvbl9jb2w9ZGYsIGNvbG9yPWNvbG9yLCBib3JkZXJfY29sb3IgPSBOQSwgYnJlYWtzPW15QnJlYWtzMykKYGBgCgoKCiMjIFBhY2thZ2UgdmVyc2lvbnMKRmlndXJlIG91dCBob3cgdG8gcHJpbnQgYWxsIHBhY2thZ2UgdmVyc2lvbnMgdXNlZC4K